using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using FineCollection.Cont;
using FineCollection.Intf;



namespace FineCollection.Const
{
    /// <summary>
    /// Extensions to other classes for working with constant collections.
    /// </summary>
    /// <stereotype>service</stereotype>
    public static class Extensions
    {
        
        /// <summary>
        /// Performs the specified action for each item from the collection.
        /// </summary>
        /// <typeparam name="T">type of items.</typeparam>
        /// <param name="collection">collection to wich items perform action.</param>
        /// <param name="action">action to perform.</param>
        public static void ForEach<T> (this RCollection<T> collection, 
                                       Action<T> action)
        {
            if (collection == null || collection.IsEmpty || action == null)
                return;

            // some optimization for ConstArray-based collections
            ConstArray<T> ca = collection as ConstArray<T>;
            if (ca != null)
            {
                for (int i = 0; i < ca.count; i++)
                    action(ca.arr[i]);
                return;
            }

            // for all other types of collections    
            foreach (T item in collection)
                action(item);
        }


        /// <summary>
        /// Makes a constant random access list by the given enumeration.
        /// </summary>
        /// <typeparam name="T">item type.</typeparam>
        /// <param name="source">enumeration to collect items from.</param>
        /// <returns>constant list.</returns>
        public static RRandomList<T> ToRList<T> (this IEnumerable<T> source)
        {
            if (source == null)
                return null;

            // fetch all
            var list = new ArrList<T>(1);
            var it = source.GetEnumerator();
            while (it.MoveNext())
                list.Add(it.Current);

            // make an appropriate list
            switch (list.Count)
            {
                case 0:
                    return ConstZero<T>.zero;
                case 1:
                    return new ConstOne<T>(list.First);
                default:
                    return new ConstList<T>(1, list.ar, list.count, true);
            }
        }



        /// <summary>
        /// Makes a constant random access sorted set by the given enumerator.
        /// </summary>
        /// <typeparam name="T">item type.</typeparam>
        /// <param name="source">enumeration to collect items from.</param>
        /// <returns>constant sorted set.</returns>
        public static RRandomSet<T> ToRSet<T>(this IEnumerable<T> source)
            where T : IComparable<T>
        {
            return ToRSet(source, 0);
        }


        /// <summary>
        /// Makes a constant random access sorted set by the given enumerator.
        /// </summary>
        /// <typeparam name="T">item type.</typeparam>
        /// <param name="source">enumeration to collect items from.</param>
        /// <param name="estimatedCount">estimated count of items will be produced by the given enumerator.</param>
        /// <returns>constant sorted set.</returns>
        public static RRandomSet<T> ToRSet<T>(this IEnumerable<T> source, int estimatedCount)
            where T: IComparable<T>
        {
            if (source == null)
                return null;

            // fetch all
            ConstUtils.ArrayWithCount<T> arr_cnt;
            using (var enumerator = source.GetEnumerator())
                arr_cnt = ConstUtils.CollectItemsIntoArray(enumerator, estimatedCount);

            // sort
            int cnt = arr_cnt.count;
            if (cnt >= 2)
            {
                Array.Sort(arr_cnt.array, 0, cnt);
                ConstUtils.removeDuplicates(arr_cnt.array, ref cnt, Comparer<T>.Default);
            }

            // make an appropriate list
            switch (cnt)
            {
                case 0:
                    return ConstOrderZero<T>.zero;
                case 1:
                    return new ConstOrderOne<T>(arr_cnt.array[0]);
                case 2:
                    return new ConstSortSet<T>(arr_cnt.array[0], arr_cnt.array[1]);
                default:
                    return new ConstSortSet<T>(arr_cnt.array, cnt, true);
            }
        }


        /// <summary>
        /// Makes a constant map from a collection or enumeration of source items.
        /// </summary>
        /// <typeparam name="X">type of the source items.</typeparam>
        /// <typeparam name="K">type of map keys.</typeparam>
        /// <typeparam name="V">type of map associated values.</typeparam>
        /// <param name="source">source items.</param>
        /// <param name="keyFunc">key part producer.</param>
        /// <param name="valueFunc">associated value part producer.</param>
        /// <param name="estimatedCount">estimated count of items in the source.</param>
        /// <returns></returns>
        public static RMap<K,V> ToRMap<X,K,V>(this IEnumerable<X> source, Func<X,K> keyFunc, Func<X,V> valueFunc, int estimatedCount)
            where K: IComparable<K>, IEquatable<K>
        {
            if (source == null)
                return null;

            // fetch all
            ConstUtils.ArrayWithCount<MapEntry<K,V>> arr_cnt;
            using (var proxy = new ProxyObjectToMapEntryEnumerator<X, K, V>(source.GetEnumerator(), keyFunc, valueFunc)) 
                arr_cnt = ConstUtils.CollectItemsIntoArray(proxy, estimatedCount);

            // sort
            int cnt = arr_cnt.count;
            if (cnt >= 2)
            {
                Array.Sort(arr_cnt.array, 0, cnt, ConstSortMap<K, V>.EntriesComparer);
                ConstUtils.removeDuplicates(arr_cnt.array, ref cnt, ConstSortMap<K,V>.EntriesComparer);
            }

            return new ConstSortMap<K,V>(arr_cnt.array, cnt, true);
        }

        

        private sealed class ProxyObjectToMapEntryEnumerator<X,K,V> : IEnumerator<MapEntry<K,V>>, IDisposable
            where K : IComparable<K>, IEquatable<K>
        {
            private readonly IEnumerator<X> sourceEnumerator;
            private readonly Func<X,K> keyFunc;
            private readonly Func<X,V> valueFunc;


            internal ProxyObjectToMapEntryEnumerator(IEnumerator<X> sourceEnumerator, Func<X,K> keyFunc, Func<X,V> valueFunc)
            {
                this.sourceEnumerator = sourceEnumerator;
                this.keyFunc = keyFunc;
                this.valueFunc = valueFunc;
            }


            public bool MoveNext()
            {
                return sourceEnumerator.MoveNext();
            }


            public void Reset()
            {
                sourceEnumerator.Reset();
            }


            object IEnumerator.Current
            {
                get { return this.Current; }
            }


            public MapEntry<K,V> Current
            {
                get
                {
                    K key = keyFunc(sourceEnumerator.Current);
                    V value = valueFunc(sourceEnumerator.Current);
                    return new MapEntry<K, V>(key, value);
                }
            }


            public void Dispose()
            {
                sourceEnumerator.Dispose();
            }

        }
    }
}